using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using IMMeDotNet;

namespace InstantMessenger.Transports
{
    public class ImMeTransport : ITransport
    {
        private readonly IDictionary<string, Contact> contacts = new Dictionary<string, Contact>();
        private readonly IDictionary<string, Connection> contactConnections = new Dictionary<string, Connection>();

        private Host host;
        private Thread thread;
        private bool signalThreadToStop;

        public string DisplayName { get { return "IM-ME Messenger"; } }

        public void Start(IEnumerable<Contact> allContacts)
        {
            foreach (var contact in allContacts)
            {
                contacts.Add(contact.Name, contact);
            }

            thread = new Thread(ConnectToImMeDongle);
            thread.Start();
        }

        public void Stop()
        {
            signalThreadToStop = true;
        }

        private void ConnectToImMeDongle(object notUsed)
        {
            var connected = false;
            var checkFrequency = new TimeSpan(0, 0, 10);

            do
            {
                try
                {
                    host = new Host();

                    connected = true;

                    host.AttemptedLogIn += host_AttemptedLogIn;
                    host.LogIn += host_LogIn;
                    host.LogOff += host_LogOff;
                    host.RequestFriendList += host_RequestFriendList;
                    host.RequestOnlineFriendList += host_RequestOnlineFriendList;
                    host.MessageReceived += host_MessageReceived;

                    return;
                }
                catch (Exception)
                {
                    Thread.Sleep(checkFrequency);
                }
            } while (!connected && !signalThreadToStop);
        }

        public bool DeliverMessage(string sender, string recipient, string message)
        {
            if (IsOnline(contacts[recipient]))
            {
                contactConnections[recipient].SendMessage(new IMMeDotNet.Contact(sender), new IMMeString(message));
                return true;
            }

            return false;
        }

        public bool IsOnline(Contact contact)
        {
            return contactConnections.ContainsKey(contact.Name);
        }

        public void AddContact(Contact contact)
        {
            // Do nothing. This class initiates this call
        }

        public void RemoveContact(Contact contact)
        {
            // Do nothing. This class initiates this call
        }

        private void host_AttemptedLogIn(object sender, AuthenticatedContactEventArgs e)
        {
            // TODO: Maybe we'll require a password at some point
            e.Success = contacts.ContainsKey(e.Contact.Username.ToString());
        }

        private void host_LogIn(object sender, ConnectionEventArgs e)
        {
            var contact = GetContact(e.Connection);
            contact.ReplaceTransport(this);
            if (contactConnections.ContainsKey(contact.Name))
                contactConnections[contact.Name] = e.Connection;
            else
                contactConnections.Add(contact.Name, e.Connection);
        }

        private void host_LogOff(object sender, ConnectionEventArgs e)
        {
            var contact = GetContact(e.Connection);
            contact.ReplaceTransport(new OfflineTransport());
            contactConnections.Remove(contact.Name);
        }

        private void host_RequestFriendList(object sender, ConnectionEventArgs e)
        {
            var contact = GetContact(e.Connection);
            e.Connection.Friends.Clear();
            var friends = from friend in contact.Friends
                          select new IMMeDotNet.Contact(friend.Name);
            e.Connection.Friends.AddRange(friends);
        }

        void host_RequestOnlineFriendList(object sender, ConnectionEventArgs e)
        {
            foreach (var friend in e.Connection.Friends)
            {
                var contact = contacts[friend.Username.ToString()];
                friend.IsOnline = contact.IsOnline;
            }
        }

        void host_MessageReceived(object sender, MessageEventArgs e)
        {
            var contact = GetContact(e.Connection);
            var recipient = contacts[e.Message.Recipient.Username.ToString()];
            recipient.DeliverMessage(contact, e.Message.Text.ToString());
        }

        private Contact GetContact(Connection connection)
        {
            return contacts[connection.Contact.Username.ToString()];
        }
    }
}